package context

import (
	"context"
	"encoding/json"
	"fmt"
	"jgt-wallet/src/logger"
	"jgt-wallet/src/schema"
	"jgt-wallet/src/util"
	"net/http"
	"strconv"
	"strings"

	"github.com/teambition/gear"
)

// WrapContext 包装上下文
func WrapContext(ctx func(*Context) error, memo ...string) gear.Middleware {
	return func(c *gear.Context) error {
		if len(memo) > 0 {
			c.SetAny(util.GetURLMemoKey(), memo[0])
		}
		return ctx(&Context{c})
	}
}

// NewContext 创建上下文
func NewContext(ctx *gear.Context) *Context {
	return &Context{
		ctx,
	}
}

// Context 上下文管理
type Context struct {
	ctx *gear.Context
}

// GearContext 获取gear上下文
func (a *Context) GearContext() *gear.Context {
	return a.ctx
}

// Background context.Background
func (a *Context) Background() context.Context {
	return context.Background()
}

// Request http request
func (a *Context) Request() *http.Request {
	return a.ctx.Req
}

// ResponseWriter http response
func (a *Context) ResponseWriter() http.ResponseWriter {
	return a.ctx.Res
}

// Header 获取头参数
func (a *Context) Header(key string) string {
	return a.ctx.GetHeader(key)
}

// Param 获取路径参数
func (a *Context) Param(key string) string {
	return a.ctx.Param(key)
}

//ParamInt 获取路径参数(Int)
func (a *Context) ParamInt64(name string, def ...int64) int64 {
	s := a.ctx.Param(name)
	v, e := strconv.ParseInt(s, 10, 64)
	if (e != nil || v == 0) && len(def) > 0 {
		return def[0]
	}
	return v
}

// Query 获取查询参数
func (a *Context) Query(name string, def ...string) string {
	v := a.ctx.Query(name)
	if v == "" && len(def) > 0 {
		return def[0]
	}
	return v
}

//QueryInt 获取查询参数(Int)
func (a *Context) QueryInt(name string, def ...int) int {
	s := a.ctx.Query(name)
	v, e := strconv.Atoi(s)
	if e != nil && len(def) > 0 {
		return def[0]
	}
	return v
}

//QueryInt64 获取查询参数(Int64)
func (a *Context) QueryInt64(name string, def ...int64) int64 {
	s := a.ctx.Query(name)
	v, e := strconv.ParseInt(s, 10, 64)
	if e != nil && len(def) > 0 {
		return def[0]
	}
	return v
}

//QueryBool 获取查询参数(bool)
func (a *Context) QueryBool(name string, def ...bool) bool {
	v, e := strconv.ParseBool(a.ctx.Query(name))
	if e != nil && len(def) > 0 {
		return def[0]
	}
	return v
}

//QueryList 获取查询参列表([]string)
func (a *Context) QueryList(name string) []string {
	return a.ctx.QueryAll(name)
}

// Redirect 302重定向
func (a *Context) Redirect(url string) error {
	return a.ctx.Redirect(url)
}

// GetPageIndex 获取分页的页索引
func (a *Context) GetPageIndex() uint {
	if v := a.Query("current"); v != "" {
		if iv := uint(util.TT(v).Uint64()); iv > 0 {
			return iv
		}
	}
	return 1
}

// GetPageSize 获取分页的页大小
func (a *Context) GetPageSize() uint {
	if v := a.Query("pageSize"); v != "" {
		if iv := uint(util.TT(v).Uint64()); iv > 0 {
			if iv > 40 {
				iv = 40
			}
			return iv
		}
	}
	return 10
}

// SetAny 设置上下文中的值
func (a *Context) SetAny(key, val interface{}) {
	a.ctx.SetAny(key, val)
}

// GetAny 获取上下文中的值
func (a *Context) GetAny(key interface{}) (interface{}, bool) {
	val, err := a.ctx.Any(key)
	if err != nil {
		return nil, false
	}
	return val, val != nil
}

// GetUserID 获取当前用户ID
func (a *Context) GetUserID() string {
	v, ok := a.GetAny(util.GetUserIDKey())
	if ok {
		return v.(string)
	}
	return ""
}

// GetTraceID 获取追踪ID
func (a *Context) GetTraceID() string {
	v, ok := a.GetAny(util.GetTraceIDKey())
	if ok {
		return v.(string)
	}
	return ""
}

// GetAPPParameter 获取APP参数
func (a *Context) GetAPPParameter() *schema.AppParameter {
	v, ok := a.GetAny(util.GetAPPParameterKey())
	if ok {
		return v.(*schema.AppParameter)
	}
	return &schema.AppParameter{}
}

// Validator 参数校验
type Validator interface {
	Validate() error
}

// ParseJSON 解析请求的JSON
func (a *Context) ParseJSON(v Validator) error {
	return a.ctx.ParseBody(v)
}

// DecodeJSON 解码JSON
func (a *Context) DecodeJSON(v interface{}) error {
	defer a.Request().Body.Close()
	return json.NewDecoder(a.Request().Body).Decode(v)
}

// ResErrBadRequest 响应请求错误
func (a *Context) ResErrBadRequest(err error, code ...int) error {
	return a.ResError(http.StatusBadRequest, err, code...)
}

// ResErrInternalServer 响应服务器错误
func (a *Context) ResErrInternalServer(err error, code ...int) error {
	return a.ResError(http.StatusInternalServerError, err, code...)
}

// ResError 响应错误
func (a *Context) ResError(statusCode int, err error, code ...int) error {
	message := "请求发生错误"
	if statusCode >= 500 {
		message = "服务器发生错误"
	}

	if err != nil {
		ss := strings.Split(err.Error(), ": ")
		if len(ss) > 0 {
			message = ss[0]
		}

		entry := logger.Access(a.GetTraceID(), a.GetUserID())
		if statusCode >= 400 && statusCode < 500 {
			entry.Warnf("[请求错误]: %s", err.Error())
		} else if statusCode >= 500 {
			entry.WithField("stack", fmt.Sprintf("%+v", err)).
				Errorf("[服务器错误]: %s", err.Error())
		}
	}

	v := map[string]interface{}{
		"message": message,
		"code":    0,
	}
	if len(code) > 0 {
		v["code"] = code[0]
	}

	return a.ResJSON(statusCode, v)
}

func (a *Context) ResHtml(data []byte) error {
	_, err := a.ResponseWriter().Write(data)
	return err
}

// ResSuccess 响应成功
func (a *Context) ResSuccess(v interface{}) error {
	return a.ResJSON(http.StatusOK, v)
}

// ResJSON 响应JSON数据
func (a *Context) ResJSON(statusCode int, v interface{}) error {
	return a.ctx.JSON(statusCode, v)
}

// ResOK 响应OK
func (a *Context) ResOK() error {
	result := struct {
		Status string `json:"status"`
	}{"ok"}
	return a.ResSuccess(result)
}

// WriteOperateLog 写入操作日志
func (a *Context) WriteOperateLog(funcName, format string, args ...interface{}) {
	logger.Operate(a.GetTraceID(), a.GetUserID()).
		WithField("operate_type", funcName).Infof(format, args...)
}

// WriteErrorLog 写入错误日志
func (a *Context) WriteErrorLog(err error) {
	if err != nil {
		logger.System(a.GetTraceID(), a.GetUserID()).
			WithField("stack", fmt.Sprintf("%+v", err)).
			Errorf("[服务器错误] %s", err.Error())
	}
}
